package com.net.xpay.common.service.impl;

import com.net.xpay.common.domain.DailyTaskRecord;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserShard;
import com.net.xpay.common.domain.stat.UserDailyStat;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.common.manager.UserShardManager;
import com.net.xpay.common.manager.stat.UserDailyStatManager;
import com.net.xpay.common.model.search.UserSearchModel;
import com.net.xpay.core.enums.SafePayOrderStatus;
import com.net.xpay.core.manager.ChargeRecordManager;
import com.net.xpay.core.manager.PayTransBatchManager;
import com.net.xpay.core.manager.PayTransManager;
import com.net.xpay.core.manager.safePay.SafePayBatchManager;
import com.net.xpay.core.manager.safePay.SafePayOrderManager;
import com.net.xpay.core.model.PartnerUserPayTransDayStatModel;
import com.net.xpay.core.model.PartnerUserSafePayTransDayStatModel;
import com.net.common.util.LocalDateUtil;
import com.net.xpay.common.service.UserDailyStatService;
import com.net.shard.ShardUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

@Service
@Slf4j
public class UserDailyStatServiceImpl implements UserDailyStatService {

    @Autowired
    private UserManager userManager;

    @Autowired
    private UserShardManager userShardManager;

    @Autowired
    private ChargeRecordManager chargeRecordManager;

    @Autowired
    private PayTransBatchManager payTransBatchManager;

    @Autowired
    private PayTransManager payTransManager;

    @Autowired
    private SafePayOrderManager safePayOrderManager;

    @Autowired
    private SafePayBatchManager safePayBatchManager;

    @Autowired
    private UserDailyStatManager userDailyStatManager;

    @Override
    public void statUserDailyStat(LocalDate day, DailyTaskRecord record) {
        //冻结的用户不统计 只统计正常的
        UserSearchModel searchModel = new UserSearchModel();
        List<Long> userIds = userManager.listIds(searchModel);

        record.setTotalCount((long) userIds.size());
        log.info("statUserDailyStat userIds={}", userIds);
        if (CollectionUtils.isEmpty(userIds)) {
            return;
        }
        for (Long userId : userIds) {
            try {
                doUserDailyStat(userId, day);
            } catch (Exception e) {
                log.error("statUserDailyStat error|userId={}", userId, e);
                record.setFailedCount(record.getFailedCount() + 1L);
                continue;
            }
            record.setSuccessCount(record.getSuccessCount() + 1L);
        }
    }

    @Override
    public void doUserDailyStat(Long userId, LocalDate day) {
        User user = userManager.getById(userId);

        //注册时间在统计时间之后的不统计
        if (user.getRegisterTime().toLocalDate().isAfter(day)) {
            return;
        }
        UserDailyStat stat = new UserDailyStat();
        // 通过userID获取到userShard
        UserShard userShard = userShardManager.getById(userId);
        try {
            // 切库处理
            ShardUtil.setShardContext(userShard.getShardId());
            // 先去判断该用户当天统计记录是否存在
            stat.setDay(day);
            stat.setYear(day.getYear());
            stat.setMonth(day.getMonthValue());
            stat.setUserId(userId);
            stat.setPartnerId(user.getPartnerId());
            boolean exitRecord = hasStated(stat);
            if (exitRecord) {
                log.warn("doUserDailyStat the record of userId={}|day={} is already exist", userId, day);
                return;
            }

            //统计数据并入库
            saveUserDailyStat(day, stat,userId);

        } finally {
            ShardUtil.cleanShardContext();
        }
    }



    private void saveUserDailyStat(LocalDate day, UserDailyStat stat,Long userId) {
        // 获取当天的起始时间和终止时间 终止时间保留在23:59:59.999999999
        LocalDateTime minEndTime = LocalDateUtil.getDayStart(day);
        LocalDateTime maxEndTime = LocalDateUtil.getDayEnd(day);
        //统计查询
        //服务费充值金额
        long serviceChargeFee = chargeRecordManager.sumUserServiceChargeFeeByDay(userId,minEndTime, maxEndTime);
        //批量有密单笔超过500金额成功笔数
        long payTransSuccessSingleVeryCount = payTransManager.statUserSingleVeryCount(userId,minEndTime, maxEndTime);
        //批量有密单笔超过500金额失败笔数
        long payTransFailSingleVeryCount = payTransManager.statUserFailSingleVeryCount(userId,minEndTime, maxEndTime);
        //批量有密单笔超过10000金额成功笔数
        long payTransSuccessLargeCount = payTransManager.statUserLargeCount(userId,minEndTime, maxEndTime);
        //批量有密超过500万账户数
        long payTransVeryAlipayUserCount = payTransBatchManager.sumUserTotalVeryAlipayUserCount(userId,minEndTime, maxEndTime);
        //安全发单笔超过500金额成功笔数
        long safePayTransSuccessSingleVeryCount = safePayOrderManager.statUserSingleVeryCount(userId,minEndTime, maxEndTime, SafePayOrderStatus.SUCCESS);
        //安全发单笔超过500金额失败笔数
        long safePayTransFailSingleVeryCount = safePayOrderManager.statUserSingleVeryCount(userId,minEndTime, maxEndTime,SafePayOrderStatus.FAIL);
        //安全发单笔超过10000金额成功笔数
        long safePayTransSuccessLargeCount = safePayOrderManager.statUserLargeCount(userId,minEndTime, maxEndTime, SafePayOrderStatus.SUCCESS);
        //安全发超过500万账户数
        long safePayTransVeryAlipayUserCount = safePayBatchManager.sumUserTotalVeryAlipayUserCount(userId,minEndTime, maxEndTime);
        //payTransBatch表统计
        PartnerUserPayTransDayStatModel companyPayTransDayStatModel = payTransBatchManager.sumPartnerUserPayTransDay(userId,minEndTime,maxEndTime);
        //safePayTransBatch表统计
        PartnerUserSafePayTransDayStatModel companySafePayTransDayStatModel = safePayBatchManager.sumPartnerUserSafePayTransDay(userId,minEndTime,maxEndTime);

        //设值
        //服务费充值金额
        stat.setServiceChargeFee(new BigDecimal(serviceChargeFee).multiply(new BigDecimal("0.01")).setScale(2, RoundingMode.HALF_UP));
        //批量有密单笔超过500金额成功笔数
        stat.setPayTransSuccessSingleVeryCount(payTransSuccessSingleVeryCount);
        //批量有密单笔超过500金额失败笔数
        stat.setPayTransFailSingleVeryCount(payTransFailSingleVeryCount );
        //批量有密单笔超过10000金额成功笔数
        stat.setPayTransSuccessLargeCount(payTransSuccessLargeCount);
        //批量有密超过500万账户数
        stat.setPayTransVeryAlipayUserCount(payTransVeryAlipayUserCount);
        //安全发单笔超过500金额成功笔数
        stat.setSafePayTransSuccessSingleVeryCount(safePayTransSuccessSingleVeryCount);
        //安全发单笔超过500金额失败笔数
        stat.setSafePayTransFailSingleVeryCount(safePayTransFailSingleVeryCount);
        //安全发单笔超过10000金额成功笔数
        stat.setSafePayTransSuccessLargeCount(safePayTransSuccessLargeCount);
        //安全发超过500万账户数
        stat.setSafePayTransVeryAlipayUserCount(safePayTransVeryAlipayUserCount);
        //payTransBatch表
        stat.setPayTransCount(companyPayTransDayStatModel.getPayTransCount());
        stat.setPayTransSuccessCount(companyPayTransDayStatModel.getPayTransSuccessCount());
        stat.setPayTransFailCount(companyPayTransDayStatModel.getPayTransFailCount());
        stat.setPayTransServicePrice(new BigDecimal(companyPayTransDayStatModel.getPayTransServicePrice()).multiply(new BigDecimal("0.01")).setScale(2, RoundingMode.HALF_UP));
        stat.setPayTransAmount(companyPayTransDayStatModel.getPayTransAmount());
        stat.setPayTransSuccessAmount(companyPayTransDayStatModel.getPayTransSuccessAmount());
        stat.setPayTransFailAmount(companyPayTransDayStatModel.getPayTransFailAmount());
        stat.setTotalPayTransBenefit(new BigDecimal(companyPayTransDayStatModel.getTotalPayTransBenefit()).multiply(new BigDecimal("0.01")).setScale(2, RoundingMode.HALF_UP));
        //safePayTransBatch
        stat.setSafePayTransCount(companySafePayTransDayStatModel.getSafePayTransCount());
        stat.setSafePayTransSuccessCount(companySafePayTransDayStatModel.getSafePayTransSuccessCount());
        stat.setSafePayTransFailCount(companySafePayTransDayStatModel.getSafePayTransFailCount());
        stat.setSafePayTransAmount(companySafePayTransDayStatModel.getSafePayTransAmount());
        stat.setSafePayTransSuccessAmount(companySafePayTransDayStatModel.getSafePayTransSuccessAmount());
        stat.setSafePayTransFailAmount(companySafePayTransDayStatModel.getSafePayTransFailAmount());
        stat.setSafePayTransServicePrice(new BigDecimal(companySafePayTransDayStatModel.getSafePayTransServicePrice()).multiply(new BigDecimal("0.01")).setScale(2, RoundingMode.HALF_UP));
        stat.setTotalSafePayTransBenefit(new BigDecimal(companySafePayTransDayStatModel.getTotalSafePayTransBenefit()).multiply(new BigDecimal("0.01")).setScale(2, RoundingMode.HALF_UP));
        //总和数据入库
        stat.setTotalCount(stat.getPayTransCount() + stat.getSafePayTransCount());
        stat.setTotalSuccessCount(stat.getPayTransSuccessCount() + stat.getSafePayTransSuccessCount());
        stat.setTotalFailCount(stat.getPayTransFailCount() + stat.getSafePayTransFailCount());

        stat.setTotalServicePrice(stat.getPayTransServicePrice().add(stat.getSafePayTransServicePrice()));

        stat.setTotalTransAmount(stat.getPayTransAmount().add(stat.getSafePayTransAmount()));
        stat.setTotalSuccessTransAmount(stat.getPayTransSuccessAmount().add(stat.getSafePayTransSuccessAmount()));
        stat.setTotalFailTransAmount(stat.getPayTransFailAmount().add(stat.getSafePayTransFailAmount()));

        stat.setTotalBenefit(stat.getTotalPayTransBenefit().add(stat.getTotalSafePayTransBenefit()));
        stat.setTotalSuccessSingleVeryCount(stat.getPayTransSuccessSingleVeryCount() + stat.getSafePayTransSuccessSingleVeryCount());
        stat.setTotalFailSingleVeryCount(stat.getPayTransFailSingleVeryCount() + stat.getSafePayTransFailSingleVeryCount());

        //单笔超过10000金额成功笔数 总和数据
        stat.setTotalSuccessLargeCount(stat.getPayTransSuccessLargeCount() + stat.getSafePayTransSuccessLargeCount());
        //入库
        userDailyStatManager.save(stat);
    }

    private boolean hasStated(UserDailyStat stat) {
        LocalDate day = stat.getDay();
        // 尝试通过时间加id获取数据
        UserDailyStat userDailyStatRecord = userDailyStatManager.findByDayAndUserId(day,stat.getUserId());
        // 若果查到了数据，表示不需要在插入记录了
        return userDailyStatRecord != null;
    }

}
